#include "uds_fp.h"
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>


#ifndef UDS_FP_DBG
#define UDS_FP_DBG(format, argument...)    do{ printf("[ %s ]  %s (line %d) -> " format"\r\n", __FUNCTION__,  __FILE__ ,__LINE__, ##argument ); }while(0)
#endif

/*
@uds 帧协议基础格式
请求参数
（json）
 		字段名   		类型  	必填  默认值   					说明
   		topic   	String  	M  				 		    消息主题
   		from    	String  	M  							源地址.sock
   		to    		String  	M  							目的地址.sock
   		id			int			M
   		params      cjson		M   NULL 					应用层传入
*/



/*******************************************************
*
* Function name :check_sum
* Description        : 和校验计算
* Parameter         ：
*        @Buf       完整的数据帧
*        @Len   	数据帧长度 
* Return          ：校验和
**********************************************************/
static uint8_t check_sum(uint8_t *Buf,uint8_t Len)
{
  uint8_t i =0;
  uint8_t sum =0;
  uint8_t checksum =0;
  
  for(i=0; i<Len; i++)
  {
    sum += *Buf++;
  }
  checksum = sum &0xff;
  return checksum;
}


/*******************************************************
*
* Function name :tx_msg_base_param_create
* Description        : 发送消息数据base创建 示例
* Parameter         ：
*        @topic       发送消息主题
*        @src         源地址.sock 
*        @dst         目的地址.sock
*        @params      应用层传入的消息参数,主要的消息域
* Return          cJSON
**********************************************************/
cJSON *tx_msg_base_param_create(char *topic,char *src, char *dst,cJSON *params)
{
	if(!topic || !src || !dst )
	{
		UDS_FP_DBG("input value is null");
		return NULL;
	}
	cJSON *mess = cJSON_CreateObject();
	cJSON_AddStringToObject(mess, "topic", topic);
	cJSON_AddStringToObject(mess, "from", src);
	cJSON_AddStringToObject(mess, "to", dst);
	if(params)cJSON_AddItemToObject(mess, "params", params);
	return mess;
}

static int frame_payload_decode(char *payload)
{
	int ret = -1;
	if(!payload )
	{
		return -1;
	}
	cJSON *msg = cJSON_Parse(payload);
	cJSON *frame_topic = cJSON_GetObjectItem(msg, "topic");
	cJSON *frame_src = cJSON_GetObjectItem(msg, "from");
	cJSON *frame_dst = cJSON_GetObjectItem(msg, "to");
	cJSON *frame_id = cJSON_GetObjectItem(msg, "id");
	cJSON *frame_param = cJSON_GetObjectItem(msg, "param");
	
EXIT:
	cJSON_Delete(msg);
	return ret;
}


/*******************************************************
*
* Function name :tx_msg_create_handle
* Description        : 发送帧缓存申请，回调注册，添加到数据监听以及发送等待应答链表
* Parameter         ：
*        @list       数据监听以及发送等待应答链表
*        @pdata        	发送数据缓存 (json)
*        @len        	发送数据长度
*        @retry_cnt     重发次数
*        @outtime_ms    超时时间
*        @cb_func       回调执行函数
* Return          ：pt_uds_fp_tx
**********************************************************/
pt_uds_fp_tx tx_msg_create_handle(pt_uds_fp_tx plist_tx,\
                                    char *pdata,\
                                    int64_t len,\
                                    uint32_t outtime_sec,\
                                    tx_callback_func tx_cb_func)
{
	if(!plist_tx)
	{
		UDS_FP_DBG("plist_tx is null");
		return NULL;
	}
	if(!pdata)
	{
		UDS_FP_DBG("pdata is null");
		return NULL;
	}
	pt_uds_fp_tx tmp =( pt_uds_fp_tx)UDS_FP_MALLOC(sizeof(t_uds_fp_tx));
	if(!tmp)
	{
		UDS_FP_DBG("tmp malloc fail ");
		goto EXIT;
	}
	memset(tmp,0x00,sizeof(t_uds_fp_tx));
	tmp->pframe = UDS_FP_MALLOC(len + sizeof(t_frame) + 1);	//因为payload中存放json字符解析需要确保最后一个字节为空。
	if(!tmp->pframe)
	{
		UDS_FP_DBG("tmp->pframe malloc fail ");
		goto EXIT;
	}
	memset(tmp->pframe ,0x00,len + sizeof(t_frame) + 1);
	tmp->tx_cb_func = tx_cb_func;
	tmp->outtime_sec = outtime_sec;
	clock_gettime(CLOCK_MONOTONIC,&tmp->occurred_time);

	tmp->pframe->head = FRAME_HEAD;
	tmp->pframe->frame_type = FRAME_TYPE_NOTIFY;
	tmp->pframe->payload_len = len ;
	memcpy(tmp->pframe->payload,pdata,len);
	tmp->pframe->check_sum = check_sum((uint8_t *)tmp->pframe->payload,tmp->pframe->payload_len);

	list_add_tail(&tmp->list,&plist_tx->list);
	return tmp;
EXIT:
	if(tmp)
	{
		if(tmp->pframe)UDS_FP_FREE(tmp->pframe);
		UDS_FP_FREE(tmp);
	}
	return NULL;
}

void tx_msg_free_handle(pt_uds_fp_tx tmp)
{
	if(tmp)
	{
		if(tmp->pframe)UDS_FP_FREE(tmp->pframe);
		UDS_FP_FREE(tmp);
	}
}

/*******************************************************
*
* Function name :ack_msg_create_handle
* Description        : 应答帧缓存申请
* Parameter         ：
*        @pdata        	发送数据缓存 (json)
*        @len        	发送数据长度
* Return         ： t_frame
**********************************************************/
t_frame *ack_msg_create_handle(char *pdata,int64_t len)
{
	if(!pdata)
	{
		UDS_FP_DBG("pdata is null");
		return NULL;
	}
	t_frame *tmp = UDS_FP_MALLOC(len + sizeof(t_frame) + 1);	//因为payload中存放json字符解析需要确保最后一个字节为空。
	if(!tmp)
	{
		UDS_FP_DBG("tmp malloc fail ");
		goto EXIT;
	}
	memset(tmp ,0x00,len + sizeof(t_frame) + 1);
	tmp->head = FRAME_HEAD;
	tmp->frame_type = FRAME_TYPE_ACK;
	tmp->payload_len = len ;
	memcpy(tmp->payload,pdata,len);
	tmp->check_sum = check_sum((uint8_t *)tmp->payload,tmp->payload_len);

	return tmp;
EXIT:
	if(tmp)
	{
		UDS_FP_FREE(tmp);
	}
	return NULL;
}

void ack_msg_free_handle(t_frame *tmp)
{
	if(tmp)
	{
		UDS_FP_FREE(tmp);
	}
}

static int get_payload_frame_id(uint8_t *payload)
{
	int id = 0;
	if(!payload){
		return -1;
	}
	cJSON *msg = cJSON_Parse(payload);
	if(msg == NULL){
		return id;
	}
	cJSON *frame_id = cJSON_GetObjectItem(msg, "id");
	if(frame_id != NULL){
		id = frame_id->valueint;
	}
	cJSON_Delete(msg);
	return id;
}

/*******************************************************
*
* Function name :tx_data_ack_handle
* Description        : 数据监听以及发送等待应答链表中对应发送帧释放且执行应答回调
* Parameter         ：
*        @plist_tx       发送链表
* Return          ：-1：失败  >= 0：成功
**********************************************************/
static int tx_data_ack_handle(struct list_head *plist_tx,t_frame *pframe_ack,uint32_t frame_ack_len)
{
	int ierro = -1;
	struct list_head *pos =NULL,*n=NULL;
	pt_uds_fp_tx tx_tmp = NULL;
    if(NULL == plist_tx)
	{
		UDS_FP_DBG("plist_tx is null");
		return -1;
	}
	if(NULL == pframe_ack)
	{
		UDS_FP_DBG("pframe_ack is null");
		return -1;
	}
	if(list_empty(plist_tx))
	{
		UDS_FP_DBG("plist_tx is empty");
		return -1;
	}
	/*遍历一遍发送任务链表，如果匹配到对应的发送应答，需要清空该发送记录*/
	list_for_each_safe(pos, n,plist_tx)
	{
		// 这里我们用list_entry来取得pos所在的结构的指针
		tx_tmp = list_entry(pos, t_uds_fp_tx, list);
		if(NULL != tx_tmp->pframe)
		{
			int tx_id = get_payload_frame_id(tx_tmp->pframe->payload);
			int ack_id = get_payload_frame_id(pframe_ack->payload);
			if(tx_id == ack_id)
			{
				//run cb
				cJSON *rx_msg = cJSON_Parse(pframe_ack->payload);
				ierro = tx_tmp->tx_cb_func(RES_SUCCESS,rx_msg);
				cJSON_Delete(rx_msg);
				tx_msg_free_handle(tx_tmp);
				list_del_init(pos);
			}
		}
	}
	return ierro;
}

/*******************************************************
*
* Function name :listen_data_rx_handle
* Description        : 数据监听链表中执行应答回调
* Parameter         ：
*        @plist_listen       监听链表
* Return          ：-1：失败  >= 0：成功
**********************************************************/
static int listen_data_rx_handle(struct list_head *plist_listen,t_frame *pframe_rx,uint32_t frame_rx_len)
{
	int ierro = -1;  
	struct list_head *pos =NULL,*n=NULL;
	pt_uds_fp_listen tmp = NULL;
    if(NULL == plist_listen)
	{
		UDS_FP_DBG("plist_listen is null");
		return -1;
	}
	if(NULL == pframe_rx)
	{
		UDS_FP_DBG("pframe_ack is null");
		return -1;
	}
	if(list_empty(plist_listen))
	{
		UDS_FP_DBG("plist_listen is empty");
		return -1;
	}
	/*遍历一遍监听链表，如果匹配到对应的监听消息，执行相关回调*/
	list_for_each_safe(pos,n, plist_listen)
	{
		// 这里我们用list_entry来取得pos所在的结构的指针
		tmp = list_entry(pos, t_uds_fp_listen, list);
		if(NULL != tmp->topic)
		{	
			cJSON *rx_msg = cJSON_Parse(pframe_rx->payload);
			if(rx_msg == NULL){
				continue;
			}
			cJSON *frame_topic = cJSON_GetObjectItem(rx_msg, "topic");
			cJSON *frame_src = cJSON_GetObjectItem(rx_msg, "from");
			cJSON *frame_dst = cJSON_GetObjectItem(rx_msg, "to");
			cJSON *frame_id = cJSON_GetObjectItem(rx_msg, "id");
			if(frame_topic == NULL || frame_src == NULL || frame_dst == NULL || frame_id == NULL){
				cJSON_Delete(rx_msg);
				continue;
			}
			if(strcmp(frame_topic->valuestring,tmp->topic) == 0){
				cJSON *ack_root;
				ack_root = cJSON_CreateObject();
				cJSON_AddStringToObject(ack_root, "topic", "ping");
				cJSON_AddNumberToObject(ack_root, "id", frame_id->valueint);
				cJSON_AddStringToObject(ack_root, "from", frame_dst->valuestring);
				cJSON_AddStringToObject(ack_root, "to", frame_src->valuestring);
				if(tmp->from_sock_path != NULL ){
					if(strcmp(frame_src->valuestring,tmp->from_sock_path) == 0){
						ierro = tmp->listen_cb_func(RES_SUCCESS,rx_msg,ack_root);
					}
				}
				else{
					ierro = tmp->listen_cb_func(RES_SUCCESS,rx_msg,ack_root);
				}

				cJSON_Delete(ack_root);
			}
			cJSON_Delete(rx_msg);
		}
	}
	return ierro;
}

/*******************************************************
*
* Function name :listen_msg_create_handle
* Description        : 监听帧缓存申请，回调注册，添加到数据监听以及发送等待应答链表
* Parameter         ：
*        @list       	数据监听以及发送等待应答链表
*        @topic     	消息说明 
*        @from			监听指定sock
*        @cb_func       回调执行函数
* Return          pt_uds_fp_listen
**********************************************************/
pt_uds_fp_listen listen_msg_create_handle(pt_uds_fp_listen plist_listen,\
                                     char *topic,\
									 char *from,\
                                    listen_callback_func listen_cb_func)
{
	if(NULL == topic)
	{
		UDS_FP_DBG("NULL == topic ");
		return NULL;
	}

	pt_uds_fp_listen tmp =( pt_uds_fp_listen)UDS_FP_MALLOC(sizeof(t_uds_fp_listen));
	if(!tmp)
	{
		UDS_FP_DBG("tmp malloc fail ");
		goto EXIT;
	}
	tmp->topic = UDS_FP_MALLOC(strlen(topic) + 1);
	if(NULL == tmp->topic)
	{
		UDS_FP_DBG("tmp->topic malloc fail ");
		goto EXIT;
	}
	memset(tmp->topic,0x00,strlen(topic) + 1);
	if(from)
	{
		tmp->from_sock_path = UDS_FP_MALLOC(strlen(from) + 1);
		if(NULL == tmp->from_sock_path)
		{
			UDS_FP_DBG("tmp->from_sock_path malloc fail ");
			goto EXIT;
		}
		memset(tmp->from_sock_path,0x00,strlen(from) + 1);
		strncpy(tmp->from_sock_path,from,strlen(from));
	}
	tmp->listen_cb_func = listen_cb_func;
	strncpy(tmp->topic,topic,strlen(topic));
	list_add_tail(&tmp->list,&plist_listen->list);
	UDS_FP_DBG("listen topic:%s ", tmp->topic);
	return tmp;
EXIT:
	if(tmp)
	{
		if(tmp->topic)UDS_FP_FREE(tmp->topic);
		if(tmp->from_sock_path)UDS_FP_FREE(tmp->from_sock_path);
		UDS_FP_FREE(tmp);
	}
	return NULL;
}

/*******************************************************
*
* Function name :listen_msg_free_handle
* Description        : 数据监听以及发送等待应答链表中对应发送帧释放
* Parameter         ：
*        @listen_list       监听链表
*		 @msg       		消息
* Return          ：-1：失败  >= 0：成功
**********************************************************/
int listen_msg_free_handle(pt_uds_fp_listen plist_listen, char *topic, char *src)
{
	struct list_head *pos =NULL,*n=NULL;
	pt_uds_fp_listen listen_tmp = NULL;
	int ret = -1;
/*遍历一遍监听链表*/
	list_for_each_safe(pos,n, &plist_listen->list)
	{
		// 这里我们用list_entry来取得pos所在的结构的指针
		listen_tmp = list_entry(pos, t_uds_fp_listen, list);
		if(NULL == listen_tmp)
		{
			break;
		}
		if(topic && src){
			if(listen_tmp->topic && listen_tmp->from_sock_path){
				if(strcmp(listen_tmp->topic,topic) == 0 && strcmp(listen_tmp->from_sock_path,src) == 0)
				{
					UDS_FP_DBG("del listen topic:%s ", listen_tmp->topic);
					list_del_init(pos);
					if(listen_tmp->topic)free(listen_tmp->topic);
					if(listen_tmp->from_sock_path)free(listen_tmp->from_sock_path);
					free(listen_tmp);
					ret = 0;
				}
			}
		}
		else if(topic){
			if(listen_tmp->topic){
				if(strcmp(listen_tmp->topic,topic) == 0 )
				{
					UDS_FP_DBG("del listen topic:%s ", listen_tmp->topic);
					list_del_init(pos);
					if(listen_tmp->topic)free(listen_tmp->topic);
					if(listen_tmp->from_sock_path)free(listen_tmp->from_sock_path);
					free(listen_tmp);
					ret = 0;
				}
			}
		
		}
		
	}
	return ret;
}

/*******************************************************
*
* Function name :frame_extraction_process
* Description        : 完整数据处理，包括校验比对，链表帧ID匹配，回调执行
* Parameter         ：
*        @frame       完整的数据帧
*        @frame_len   数据帧长度 
*        @list        发送等待应答链表
*        @list        数据监听链表
* Return          ：-1：校验或者回调返回错误，>= 0：正常
**********************************************************/
static int frame_extraction_process(t_frame *frame,uint32_t frame_len,struct list_head *tx_list,struct list_head *listen_list)
{
	int ierro = -1;
	if(NULL == frame)
	{
		UDS_FP_DBG("NULL == frame");
		return -1;
	}

	if(check_sum(frame->payload,frame->payload_len) != frame->check_sum)
	{
		UDS_FP_DBG("check_sum fail.");
		return -1;
	}
#if 0
	printf("recv:%s\r\n",frame->payload);
#endif	
	switch (frame->frame_type)
	{
		case FRAME_TYPE_NOTIFY:
			listen_data_rx_handle(listen_list,frame,frame_len);
			break;
		case FRAME_TYPE_ACK:
			tx_data_ack_handle(tx_list,frame,frame_len);
			break;
		default:
			break;
	}
	return ierro;
}




/*******************************************************
*
* Function name :rx_data_decode_handle
* Description        : 接收uds原始数据断帧处理函数
* Parameter         ：
*        @pbuff       接收数据缓存
*        @len         接收数据长度 
*        @list        发送等待应答链表
*        @list        数据监听链表
* Return          ：void
**********************************************************/
void rx_data_decode_handle(uint8_t *pbuff,int64_t len,struct list_head *tx_list,struct list_head *listen_list)
{
    static uint8_t s_rxbuff[MTU_MAX] = {0};
    static uint32_t s_rxsize = 0;
    uint32_t remain_len = 0;
    uint32_t frame_len = 0;
    uint8_t *temp_buf = pbuff;
    uint32_t readsize = len;
    uint32_t cnt = 0;

    if(NULL == pbuff )
        return ;
        
    if(readsize > 0)
    {
    	memcpy(&s_rxbuff[s_rxsize], temp_buf, readsize);
    	s_rxsize += readsize;
    	remain_len = s_rxsize;

    	while(remain_len > 0)
    	{
            /*
                递进拆包处理：先匹配帧头，然后判断长度是否满足帧固定部分长度，接着判断是否满足整帧长度，最后检查校验
            */
    		if(s_rxbuff[cnt] == FRAME_HEAD) 
    		{
    			if(remain_len > sizeof(t_frame))
    			{
                    t_frame *frame = (t_frame *)&s_rxbuff[cnt];
    				frame_len = frame->payload_len + sizeof(t_frame);
    				if(remain_len >= frame_len)
    				{
    					frame_extraction_process(frame, frame_len,tx_list,listen_list);
    					remain_len -= (frame_len - 1);
    					cnt += (frame_len - 1);
    				}
    				else
    				{
    					memcpy(s_rxbuff, frame, remain_len);
    					s_rxsize = remain_len;
    					break;
    				}
    			}
    			else
    			{
    				memcpy(s_rxbuff, &s_rxbuff[cnt], remain_len);
    				s_rxsize = remain_len;
    				break;
    			}
    		}

    		cnt++;
    		remain_len--;
    		s_rxsize = remain_len;
    	}
    }
}

/*******************************************************
*
* Function name :tx_data_ack_timeout_handle
* Description        : 发送帧应答超时处理
* Parameter         ：
*        @plist_tx       发送链表
* Return          ：-1：失败  >= 0：成功
**********************************************************/
int tx_data_ack_timeout_handle(struct list_head *plist_tx)
{
	int ierro = -1;
	struct list_head *pos =NULL,*n=NULL;
	pt_uds_fp_tx tx_tmp = NULL;
	struct timespec ts;
	clock_gettime(CLOCK_MONOTONIC, &ts);
    if(NULL == plist_tx)
	{
		UDS_FP_DBG("plist_tx is null");
		return -1;
	}

	if(list_empty(plist_tx))
	{
		//UDS_FP_DBG("plist_tx is empty");
		return -1;
	}
	/*遍历一遍发送任务链表，如果匹配到对应的发送应答，需要清空该发送记录*/
	list_for_each_safe(pos, n,plist_tx)
	{
		// 这里我们用list_entry来取得pos所在的结构的指针
		tx_tmp = list_entry(pos, t_uds_fp_tx, list);
		//UDS_FP_DBG("=====pos=%p====tx_tmp=%p==\n",pos,tx_tmp);
		//UDS_FP_DBG("pt_uds_fp_tx->frame:%p ", tx_tmp->pframe);
		if(NULL != tx_tmp->pframe)
		{
			//TIMEOUT
			if(tx_tmp->occurred_time.tv_sec < ts.tv_sec || tx_tmp->occurred_time.tv_sec - ts.tv_sec > tx_tmp->outtime_sec)
			{
				//run cb
				ierro = tx_tmp->tx_cb_func(RES_TIMEOUT,NULL);
				tx_msg_free_handle(tx_tmp);
				list_del_init(pos);
			}
		}
	}
	return ierro;
}

